#ifndef ETH_IMPL_H
#define ETH_IMPL_H

#include <memory>
#include <string.h>

#include "common_defs.h"
#include "framebuf_mgr.h"
#include "ifconfig.h"
#include "endpoint_addr.h"

#define MIN_ETH_HDR     14
#define MIN_ETH_PDU     64

namespace usnet {

enum class FilterLevel {
    IP4_FULL,
    IP4_LOCAL,
    IP6_FULL,
    IP6_LOCAL,
    ETH_FULL,
    ETH_LOCAL,
};

class EthImpl {
public:
    ~EthImpl()
    {
        ef_driver_close(ef_res_.handle_);
        fb_mgr_ = nullptr;
        if (evtab_) {
            delete[] evtab_;
        }
    }
    
    static std::unique_ptr<EthImpl>
    MakeUnique(
        const PPT proto,
        const EndpointAddr* laddr,
        const EndpointAddr* raddr,
        const Ifconfig* ifcfg);

    std::shared_ptr<FrameBufMgr> PrefillHeader();
    
    void SendFrame();
    
    bool RecvFrame();

    bool AddFilter(const FilterLevel fl);

    inline uint32_t GetHeaderSize() const
    {
        return hdr_size_;
    }

    inline std::shared_ptr<FrameBufMgr> GetFrameBufMgr() const
    {
        return fb_mgr_;
    }
    
private:
    EthImpl(
        const PPT proto,
        const EndpointAddr* laddr,
        const EndpointAddr* raddr,
        const Ifconfig* ifcfg):
        proto_ { proto },
        laddr_ { laddr },
        raddr_ { raddr },
        ifcfg_ { ifcfg },
        evnum_ { (ifcfg_->txqlen_ << 1) }
    {
        evtab_ = new(std::nothrow) ef_event[evnum_];
    }
    
    bool InitEFResource();
    bool InitFameBufMgr();
    
    const PPT           proto_ { PPT::NONE };
    const EndpointAddr* laddr_ { nullptr };
    const EndpointAddr* raddr_ { nullptr };
    const Ifconfig*     ifcfg_ { nullptr };
    const uint32_t      hdr_size_ { MIN_ETH_HDR };
    const uint32_t      evnum_ { 0 };
    ef_event*           evtab_ { nullptr };
    
    EFResource ef_res_;
    std::shared_ptr<FrameBufMgr> fb_mgr_ { nullptr };
};
} // end of namespace usnet
#endif